조건문의 단순화

1. Decompose Conditional

복잡한 조건문이 있는 경우, 조건, then 부분, 그리고 else 부분에서 메소드를 추출하라.

변경 전변경 후
{code}
if(data.before(SUMMER_START)
data.after(SUMMER_END))
charge = quantity * _winterRate + _winterServiceCharer;
else
charge = quantity * _summerRate;
{code}
{code}
if(notSUmmer(date))
charge = winterCharge(quantity);
else
charge = summerCharge(quantity);
{code}

절차

  • 조건을 하나의 메소드로 뽑아낸다.
  • then부분과 else 부분을 각각의 메소드로 뽑아낸다.

2. Consolidate Conditional Expression

같은 결과를 초래하는 일련의 조건 테스트가 있는 경우, 그것을 하나의 조건식으로 결합하여 뽑아내라.

변경 전변경 후
{code}
doubul sdisabilityAmount(){
if(_seniority < 2) return 0;
if(_monthsDisabled > 12) return 0;
if(_isPatrTime) return 0;
{code}
{code}
double disabulityAmont(){
if(isNotEligableForDisability()) return 0;
{code}

절차

  • 부작용을 가지고 있는 조건문이 있는지 확인한다.
  • 여러개의 조건문을 논리연산자를 사용하는 하나의 조건문으로 대체한다.
  • 컴파일, 테스트를 한다.
  • 조건에 대해 Extract Method를 사용하는 것을 고려한다.

3. Consolidate Duplicate Conditional Fragments

동일한 코드 조각이 조건문의 모든 분기 안에 있는 경우, 동일한 코드를 조건문 밖으로 옮겨라.

변경 전변경 후
{code}
if(isSpecialDeal()){
total = price * 0.95;
send();
}else{
total = price * 0.98;
send();
}
{code}
{code}
if(isSpecialDeal())
total = price * 0.95;
else
total = price * 0.98;
send();
{code}

절차

  • 조건에 상관없이 동일하게 실행되는 코드를 확인한다.
  • 공통으로 사용되는 코드가 시작 부분에 있다면, 그 코드를 조건문 앞으로 옮긴다.
  • 공통으로 사용되는 코드가 끕부분에 있다면, 그 코드를 조건문뒤로 옮긴다.
  • 공통으로 사용되는 코드가 중간 부분에 있다면, 그 코드의 앞 또는 뒤에 있는 코드와 위치를 바꿀 수 있는지를 살펴본다.
    만약, 가능하다면, 공통으로 사용하는 코드를 앞이나 뒤쪽끝으로 옮기는 것이 가능하다.
  • 만약 한문장보다 많다면, 그 코드를 메소드로 추출해야한다.

4. Remove Control Flag

일련의 boolean 식에서 컨트롤 플래그 역할을 하는 변수가 있는경우, break 또는 return 을 대신 사용하라.

변경 전변경 후
{code}
void checkSecurity(String[] people){
boolean found = false;
for(int i=0; i<people.length; i++){
if(!found){
if(people[i].equals("Don")){
sendAlert();
found = true;
}
if(people[i].equals("John")){
sendAlert();
found = true;
}
}
}
}
{code}
{code}
void checkSecurity(String[] people){
boolean found = false;
for(int i=0; i<people.length; i++){
if(!found){
if(people[i].equals("Don")){
sendAlert();
break;
}
if(people[i].equals("John")){
sendAlert();
break;
}
}
}
}
{code}
변경 전변경 후
{code}
void checkSecurity(String[] people){
boolean found = false;
for(int i=0; i<people.length; i++){
if(!found){
if(people[i].equals("Don")){
sendAlert();
found = "Don";
}
if(people[i].equals("John")){
sendAlert();
found = "John";
}
}
}
}
{code}
{code}
void checkSecurity(String[] people){
boolean found = false;
for(int i=0; i<people.length; i++){
if(!found){
if(people[i].equals("Don")){
sendAlert();
return "Don";
}
if(people[i].equals("John")){
sendAlert();
return "John";
}
}
}
}
{code}

절차

  • 논리문밖으로 나오도록 하는 컨트롤 플래그의 값을 찾는다.
  • 컨트롤 플래그 값을 설정하던 부분을 찾아 break, continue로 변경한다.
  • 컴파일, 테스트를 한다.

5. Replace Nested Conditional with Guard Clauses

메소드가 정상적인 실행경로를 불명확하게 하는 조건 동작을 가지고 있는경우, 모든 특별한 경우에 대해서 보호절을 사용하라.

변경 전변경 후
{code}
double getPayAmount(){
double result;
if(_isDead) result = deadAmount();
else{
if(_isSeparated) result = seperatedAmount();
else{
if(_isRetired) result = retiredAmount();
else result = normalPayAmount();
}
}
return result;
}
{code}
{code}
double getPayAmount(){
if(_isDead) return deadAmount();
if(_isseparated) return separatedAmount();
if(_isRetired) return retiredAmount();
return normalPayAmount();
}
{code}

절차

  • 각각의 검사에 보호절을 넣어라.
  • 각각의 검사부분을 보호절로 바꾼후 컴파일, 테스트를 한다.

6. Replace Conditional with Polymorphism

객체의 타입에 따른 다른 동작을 선택하는 조건문을 가지고 있는 경우, 조건문의 각 부분을 서브클래스에 있는 오버라이딩 메소드로 옮겨라.
그리고 원래 메소드를 abstrace로 만들어라.


double getSpeed(){
	switch(_type){
		case EUROPEAN:
			return getBaseSpeed();
		case AFRIACN:
			return getBaseSpeed() - getLoadFactor() * _numberOfCoconuts;
		case NORWEGIAN_BLUE:
			return(_isNailed) ? 0 : getBaseSpeed(_voltage);
	}
}

절차

  • 조건문이 큰 메소드의 한 부분이라면, 그 조건문 부분을 취해서 Extract Method를 사용한다.
  • 서브클래스중에 하나를 선택하여 조건문을 상속구조의 제일 위에 위치시킨다.
  • 서브클래스중에 하나를 선택하여 조건문 메소드를 오버라이드 하는 서브클래스 메소드를 만든다.
    조건문에서 필요한 부분을 서브클래스에 복사하고 적절히 수정한다.
  • 컴파일, 테스트를 한다.
  • 조건문에서 복사한 부분을 제거한다.
  • 컴파일 테스트를 한다.
  • 조건문의 모든 조건 부분이 서브클래스 메소드를 바뀔때까지 이작업을 한다.
  • 수퍼클래스 메소드를 abstract로 만든다.

7. Introduce Null Object

null 체크를 반복적으로 하고 있다면, null값을 null객체로 대체하라.


class NullCustomer extends Customer{
  public boolean isNull(){
	return true;
  }
}

class Customer...
  static Customer newNull(){
    return new NullCustomer();
  }
}

class Site...
  Customer getCustomer(){
    return(_customer == null) ?
      Customer.newNull():
      _customer;
  }
  
  
class Site...
  Customer getCustomer(){
    return(_customer.isNull()) ?
      Customer.newNull():
      _customer;
  }
  

//조건문을 제거함으로써 얻는 이익  
  
String customerName;
if(customer.isNull()) customerName = "occupant";
else customerName = customer.getName();

//NullCustomer 클래스에 추가
class NullCustomer..
  public String getName(){
    return "occupant";
  }

//위에 조건문을 없앤다.
String customerName = customer.getName();



8. Introduce Assertion

코드의 한 부분이 프로그램의 상태에 대하여 어떤것을 가정하고 있으면, assertion을 써서 가정을 명시되게 만들어라.

변경 전변경 후
{code}
double getExpenseLimit(){
return(_expenseLimit != NULL_EXPENSE) ?
_expenseLimit;
_primaryProject.getMemberExpenseLimit();
}
{code}
{code}
double getExpenseLimit(){
Assert.isTrue(_expenseLimit != NULL_EXPENSE
_primaryProject != null);
return (_expenseLimit != NULL_EXPENSE) ?
_expenseLimit;
_primaryProject.getMemberExpenseLimit();
}
{code}

문서에 대하여